#include <string.h>
#include <iostream>

using std::cout;
using std::endl;
using std::ostream;

class CowString {
    class CharProxy
    {
    public:
        CharProxy(CowString & self, int idx)
        : _self(self)
        , _idx(idx)
        {}

        //CharProxy & operator=(const CharProxy &);
        char & operator=(const char & ch);

        operator char()
        {   
            cout << "operator char()" << endl;
            return _self._pstr[_idx];   
        }

    private:
        CowString & _self;
        int _idx;
    };
public:
    CowString()
    : _pstr(new char[1 + 4]() + 4)
    {   
        cout << "CowString()" << endl;  
        //初始化引用计数的值
        initRefcount();
    }

    CowString(const char * pstr)
    : _pstr(new char[strlen(pstr) + 1 + 4]() + 4)
    {
        cout << "CowString(const char *)" << endl;
        strcpy(_pstr, pstr);
        initRefcount();
    }

    CowString(const CowString & rhs)
    : _pstr(rhs._pstr)// O(1)
    {
        cout << "CowString(const CowString&)" << endl;
        increaseRefcount();//O(1)
    }

    CowString & operator=(const CowString & rhs)
    {
        cout << "CowString& operator=(const CowString&)" << endl;
        if(this != &rhs) {//自赋值
            //释放左操作数申请的空间
            release();
            _pstr = rhs._pstr;//浅拷贝
            increaseRefcount();//引用计数加1
        }
        return *this;
    }

    ~CowString()
    {
        //cout << "~CowString()" << endl;
        release();
    }

    const char * c_str() const 
    {   return _pstr;   }

    int getRefCount() const 
    {   return *(int*)(_pstr - 4);  }

    //问题:下标访问运算符的返回值 不知道接下来要执行的是
    //读操作还是写操作
#if 0
    char & operator[](int idx)
    {
        if(idx >=0 && idx < size()) {
            //写操作(进行修改时，要深拷贝)
            if(getRefCount() > 1) {
                //共享字符串空间执行深拷贝
                char * ptmp = new char[size() + 1 + 4]() + 4;
                strcpy(ptmp, _pstr);
                decreaseRefcount();//改变指向之前，原来共享字符串空间的引用计数要减1
                _pstr = ptmp;//改变指向
                initRefcount();//初始化新空间中的引用计数
            }
            return _pstr[idx];
        } else {
            static char ch = '\0';
            return ch;
        }
    }
#endif
    CharProxy operator[](int idx)
    {   return CharProxy(*this, idx); }

    int size() const 
    {   return strlen(_pstr);   }

private:
    void release()
    {
        //先将引用计数减一
        decreaseRefcount();
        //当引用计数减为0时，才真正释放字符串内容所在的空间
        if(getRefCount() == 0) {
            delete [] (_pstr - 4);
            _pstr = nullptr;
            cout << ">> delete heap space" << endl;
        }
    }

    void initRefcount()
    {   *(int*)(_pstr - 4) = 1;  }

    void increaseRefcount()
    {   ++*(int*)(_pstr - 4);   }

    void decreaseRefcount()
    {   --*(int*)(_pstr - 4);   }

    friend ostream & operator<<(ostream & os, const CowString & rhs);
private:
    char * _pstr;//指向的是字符串内容的首地址
};

char & CowString::CharProxy::operator=(const char & ch)
{
    //该函数还是在CowString的内部，所以可以访问私有成员
    if(_idx >=0 && _idx < _self.size()) {
        cout <<" CharProxy::operator=()" << endl;
        //写操作(进行修改时，要深拷贝)
        if(_self.getRefCount() > 1) {
            //共享字符串空间执行深拷贝
            char * ptmp = new char[_self.size() + 1 + 4]() + 4;
            strcpy(ptmp, _self._pstr);
            _self.decreaseRefcount();//改变指向之前，原来共享字符串空间的引用计数要减1
            _self._pstr = ptmp;//改变指向
            _self.initRefcount();//初始化新空间中的引用计数
        }
        //执行写操作
        _self._pstr[_idx] = ch;
        return _self._pstr[_idx];
    } else {
        cout << "out of range" << endl;
        static char ch = '\0';
        return ch;
    }
}

ostream & operator<<(ostream & os, const CowString & rhs)
{
    os << rhs._pstr;
    return os;
}

void test0()
{
    CowString str1;
    cout << "str1's refcount: " << str1.getRefCount() << endl;
    CowString str2 = str1;
    cout << "str1:" << str1 << endl;
    cout << "str2:" << str2 << endl;
    cout << "str1's refcount: " << str1.getRefCount() << endl;
    cout << "str2's refcount: " << str2.getRefCount() << endl;
    cout << endl;

}

void test1()
{
    CowString str3 = "hello,world";
    cout << "str3' refcount:" << str3.getRefCount() << endl;

    CowString str4 = str3;
    cout << "str3:" << str3 << endl;
    cout << "str4:" << str4 << endl;
    cout << "str3' refcount:" << str3.getRefCount() << endl;
    cout << "str4' refcount:" << str4.getRefCount() << endl;
    printf("str3' c_str: %p\n", str3.c_str());
    printf("str4' c_str: %p\n", str4.c_str());

    //str3 = str3;//自赋值
}

void test2()
{
    CowString str1 = "hello,world";
    cout << "str1' refcount:" << str1.getRefCount() << endl;
    CowString str2 = str1;
    cout << "str1' refcount:" << str1.getRefCount() << endl;
    cout << "str2' refcount:" << str2.getRefCount() << endl;

    CowString str3 = "hello";
    cout << "str3:" << str3 << endl;
    cout << "str3' refcount:" << str3.getRefCount() << endl;
    cout << endl << "执行赋值操作 str3 = str4之后:" << endl;
    
    CowString str4 = "world";

    str3 = str4;
    cout << "str3:" << str3 << endl;
    cout << "str4:" << str4 << endl;
    cout << "str3' refcount:" << str3.getRefCount() << endl;
    cout << "str4' refcount:" << str4.getRefCount() << endl;
    printf("str3' c_str: %p\n", str3.c_str());
    printf("str4' c_str: %p\n", str4.c_str());

    CowString str5 = "hello,world";
    cout << "str5:" << str5 << endl;
    cout << "str5' refcount:" << str5.getRefCount() << endl;

    cout << endl << "执行赋值操作 str2 = str4之后:" << endl;
    str2 = str4;
    cout << "str1:" << str1 << endl;
    cout << "str1' refcount:" << str1.getRefCount() << endl;
    cout << "str2:" << str2 << endl;
    cout << "str2' refcount:" << str2.getRefCount() << endl;
    cout << "str4:" << str4 << endl;
    cout << "str4' refcount:" << str4.getRefCount() << endl;

    cout << endl << "执行读操作 str1[0]:" << endl;
    cout << "str[1]: " << str1[0] << endl;

    cout << endl << "执行写操作 str2[0] = 'X':" << endl;
    str2[0] = 'X';
    cout << "str2[0]:" << str2[0] << endl;
    cout << "str3[0]:" << str3[0] << endl;
    //cout << "str4[0]:" << str4[0] << endl;
    cout << "str2' refcount:" << str2.getRefCount() << endl;
    cout << "str3' refcount:" << str3.getRefCount() << endl;
    cout << "str4' refcount:" << str4.getRefCount() << endl;
}


int main()
{
    /* test1(); */
    test2();


    return 0;
}

